package sweet.helper

import java.io._
import scala.io.Source

object FileHelper{
  val DEFAULT_BLOCK_SIZE = 1024*8
  implicit def file2helper(file: File) = new FileHelper(file)  
}

import FileHelper.DEFAULT_BLOCK_SIZE

/** Java File extension.
 */
class FileHelper(file: File, blockSize: Int) {
  def this(name: String) = this(new File(name), DEFAULT_BLOCK_SIZE)
  def this(file: File) = this(file, DEFAULT_BLOCK_SIZE)
  
  def parseFileExt(sep: String): (String, String) = { 
    val name = file.getName
    val idx = name.lastIndexOf(sep)
    (name.substring(0, idx), name.substring(idx+1))
  }
  def getExt = parseFileExt(".")._1
  def getBasename = parseFileExt(".")._2
  
  /** Get only the path upto where it first defined if not abosulte.
   */
  def getPathname: String = {
    if(file.isAbsolute) file.getAbsolutePath
    else{
      var f = file //init file path name
      val sb = new StringBuilder(f.getName)
      while(f.getParentFile != null){
        f = f.getParentFile
        sb.insert(0, f.getName+File.separator)
      }
      sb.toString
    }
  }
  
  /** List file in descending order. */
  def listSortedFiles: List[File] = listSortedFiles((f1,f2)=>f1.compareTo(f2)<0)
  
  /** List file according to your own comparator */
  def listSortedFiles(cmp: (File,File)=>Boolean): List[File] = {    
    val files = file.listFiles.toList
    files.sort(cmp)
  }
  
  def walk(func: File=>Unit){
    def walk(walkedFile: File){
      if(walkedFile.isDirectory){
        val files = walkedFile.listFiles.toList
        //don't use #listSortedFiles as it needs implicit conversion, thus avoiding performance hit.
        for(subfile <- files.sort((f1,f2)=>f1.compareTo(f2)<0) )
          walk(subfile)
      }
      func(walkedFile)
    }
    walk(file)
  }
  
  def eachFile(func: File=>Unit){
    walk{ f =>  if(f.isFile) func(f) }
  }
  def eachDir(func: File=>Unit){    
    walk{ f =>  if(f.isDirectory) func(f) }
  }
  def deleteAll{
    walk{ f => f.delete }
  }    
  def copyTo(dest:File)={
    if(!file.isFile) throw new Exception("Source is not a file.")
    
    //if dest is a directory, then copy into it. else overwrite dest file.
    if(dest.isDirectory){
      val destFile = new File(dest, file.getName)
      StreamHelper.copyStream(new FileInputStream(file), new FileOutputStream(destFile), blockSize)
    }else{
      StreamHelper.copyStream(new FileInputStream(file), new FileOutputStream(dest), blockSize)    
    }
  }
  def eachLine(process: (String)=>Unit){
		StreamHelper.eachLine(new FileInputStream(file)){ ln => process(ln) }
  }
  
  def eachLineWithNumber(func: (String, Int)=>Unit){
    var i = 0
    eachLine{ ln => i += 1; func(ln, i) }    
  }
  
  def eachByte(process: (Byte)=>Unit){
    eachBlock(blockSize){ buf => for(b <- buf) process(b) }
  }
  
  def eachBlock(size:Int)(process: (Array[Byte])=>Unit){
		StreamHelper.eachBlock(new FileInputStream(file), size){ buf => process(buf) }
  }
  def writeText(text: String)={
    withPrintWriter{ out => out.println(text) }
  }  
  //NewLine is expected on input.
  def writeLines(lines: Iterator[String]){
    withPrintWriter{ out => for(ln <- lines) out.println(ln) }
  }  
  def withWriter(func: Writer=>Unit){
    val writer = new BufferedWriter(new FileWriter(file));
    try{ func(writer)}
    finally{ writer.close }
  }  
  def withPrintWriter(func: PrintWriter=>Unit){
    withWriter{ writer =>
      val out = new PrintWriter(writer)
      try{ func(out)}
      finally{ out.close }
    }
  }
  def readText: String = {
    val sep = File.separator
    val sb = new StringBuilder()
    StreamHelper.eachLine(new FileInputStream(file)){ ln => sb.append(ln); sb.append(sep) }
    sb.toString
  }
  
  def readLines: List[String] = {
    val lb = new scala.collection.mutable.ListBuffer[String]
    StreamHelper.eachLine(new FileInputStream(file)){ ln => lb.append(ln) }
    lb.toList
  }
}
